package net.mimai.vultrue.bus.excel;

import net.mimai.vultrue.framework.base.exception.BusinessException;
import net.mimai.vultrue.framework.base.platform.FrameworkEnvironment;
import net.mimai.vultrue.framework.logger.rr.WebfluxServerResponseDecorator;
import net.mimai.vultrue.framework.utils.FrameworkUtils;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ZeroCopyHttpOutputMessage;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

/**
 * @ClassName ExcelGenerator
 * @Description TODO
 * @Author 方丈
 * @Date 2021/4/16 14:39
 * @Version 1.0.0
 **/
@Component
public class ExcelReportEngine<T> {
    @Resource
    FrameworkEnvironment frameworkEnvironment;

    String baseDownloadPath;

    @PostConstruct
    public void init(){
        String eglHome =frameworkEnvironment.getWorkBaseHome();

        baseDownloadPath =eglHome+"downloadFiles"+File.separator;
        File baseDir=new File(baseDownloadPath);
        if(!baseDir.exists()){
            baseDir.mkdirs();
        }
    }

    public Mono<Void> execute(IExcelAdapter excelAdapter,T conditionBean,ServerHttpResponse response) {
        try {
            return Mono.fromCallable(() -> generateExcel(excelAdapter,conditionBean)).flatMap(file ->this.send(response, file)) ;
        }catch (BusinessException be){
            throw be;
        }catch (Exception e){
            FrameworkUtils.throwExRet(e);
        }
        return Mono.empty();
    }

    private File generateExcel(IExcelAdapter excelAdapter,T conditionBean) throws Exception {
        String fileName =excelAdapter.fileName();
        File file =new File(baseDownloadPath.concat(fileName));
        try {
            // 创建工作簿对象
            SXSSFWorkbook workbook = new SXSSFWorkbook(100);
            // 创建工作表
            SXSSFSheet sheet = workbook.createSheet(excelAdapter.sheetName());
            excelAdapter.createHeader(sheet);
            excelAdapter.buildBody(sheet,conditionBean);
            excelAdapter.appendTail(sheet);
            try (OutputStream out = new FileOutputStream(file)){
                workbook.write(out);
            } catch (Exception e){
                throw e;
            }
        }catch (Exception e) {
            throw e;
        }
        return file;
    }

    public Mono<Void> executeManySheet(IExcelAdapter excelAdapter,T conditionBean,ServerHttpResponse response) {
        try {
            return Mono.fromCallable(() -> generateExcelManySheet(excelAdapter,conditionBean)).flatMap(file ->this.send(response, file)) ;
        }catch (BusinessException be){
            throw be;
        }catch (Exception e){
            FrameworkUtils.throwExRet(e);
        }
        return Mono.empty();
    }

    private File generateExcelManySheet(IExcelAdapter excelAdapter,T conditionBean) throws Exception {
        String fileName =excelAdapter.fileName();
        File file =new File(baseDownloadPath.concat(fileName));
        try {
            // 创建工作簿对象
            SXSSFWorkbook workbook = new SXSSFWorkbook(100);
            int sheetNum = excelAdapter.sheetNum();
            for (int i = 0; i < sheetNum; i++) {
                // 创建工作表
                SXSSFSheet sheet = workbook.createSheet(excelAdapter.sheetName());
                excelAdapter.createHeader(sheet);
                excelAdapter.buildBody(sheet,conditionBean);
                excelAdapter.appendTail(sheet);
            }
            try (OutputStream out = new FileOutputStream(file)){
                workbook.write(out);
            } catch (Exception e){
                throw e;
            }
        }catch (Exception e) {
            throw e;
        }
        return file;
    }

    private Mono<Void> send(ServerHttpResponse response, File file) {
        ZeroCopyHttpOutputMessage zeroCopyHttpOutputMessage;
        if(response instanceof WebfluxServerResponseDecorator){
            zeroCopyHttpOutputMessage = (ZeroCopyHttpOutputMessage) (((WebfluxServerResponseDecorator) response).getDelegate());
        }else{
            zeroCopyHttpOutputMessage = (ZeroCopyHttpOutputMessage) (response);
        }
        try {
            response.getHeaders()
                    .set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=".concat(
                            URLEncoder.encode(file.getName(), StandardCharsets.UTF_8.displayName())));
            response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
            response.getHeaders().setContentLength(file.length());
            return zeroCopyHttpOutputMessage.writeWith(file, 0, file.length());
        } catch (UnsupportedEncodingException e) {
            throw new UnsupportedOperationException();
        }
    }
}
